package org.xx.armory.spring5.mvc;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.HashMap;
import java.util.Map;

import static java.util.Map.copyOf;
import static org.xx.armory.commons.ObjectUtils.isNullOrBlank;
import static org.xx.armory.commons.Validators.rejectIfBlank;

/**
 * 业务逻辑错误。
 *
 * 业务逻辑错误包含错误代码和错误消息。在同一个业务操作中，错误代码应当不同。在不同的业务操作中，错误代码可以相同。
 */
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class BusinessLogicException
        extends RuntimeException {
    private static final long serialVersionUID = 1L;

    private final int code;
    private final Map<String, Object> extras;

    /**
     * 初始化业务逻辑错误。
     *
     * @param code
     *         错误代码。
     */
    public BusinessLogicException(
            int code
    ) {
        this(code, "ABE-" + code);
    }

    /**
     * 初始化业务逻辑错误。
     *
     * @param code
     *         错误代码。
     * @param message
     *         错误消息。
     */
    public BusinessLogicException(
            int code,
            String message
    ) {
        this(code, message, null);
    }

    /**
     * 初始化业务逻辑错误。
     *
     * @param code
     *         错误代码。
     * @param message
     *         错误消息。
     * @param cause
     *         导致此错误的原因。
     */
    public BusinessLogicException(
            int code,
            String message,
            Throwable cause
    ) {
        super(message, cause);

        this.code = code;
        this.extras = new HashMap<>();
    }

    /**
     * 添加一条附加数据。
     *
     * 如果附加数据的值是{@code null}或者空字符串则
     *
     * @param name
     *         附加数据的名字。
     * @param value
     *         附加数据的值。
     * @return 此异常对象本身。
     */
    public final BusinessLogicException addExtra(
            String name,
            Object value
    ) {
        name = rejectIfBlank(name, "key").trim();

        if (this.extras.containsKey(name)) {
            throw new IllegalArgumentException("Duplicated key: " + name);
        }

        if (!isNullOrBlank(value)) {
            this.extras.put(name, value);
        }

        return this;
    }

    /**
     * 获取错误码。
     *
     * @return 错误码。
     */
    public final int getCode() {
        return this.code;
    }

    /**
     * 获取错误附加数据。
     *
     * @return 错误附加数据。
     */
    public final Map<String, Object> getExtras() {
        return copyOf(this.extras);
    }
}
